﻿using System;
using System.IO;
using System.Threading;

using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace GroundStation.fdr
{
    class ClearSD
    {
        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern SafeFileHandle CreateFile(
            string fileName,
            [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
            [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
            int securityAttributes,
            [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
            [MarshalAs(UnmanagedType.U4)] FileAttributes fileAttributes,
            IntPtr template);

        private SafeFileHandle writeHandle;
        private SafeFileHandle readHandle;
        private string _sdDrive;
        private Thread ClearThread;
        private bool _isIn = false;
        private bool _notTotal = true;
        
        public bool isClearSDin()
        {
            return _isIn;
        }

        public bool Stop()
        {
            if (_isIn)
            {
                _isIn = false;
                if (_isIn)
                    return false;
                else
                    return true;
            }
            else
            return true;
        }

        private bool clearSDInit()
        {
            writeHandle = CreateFile(_sdDrive,
                FileAccess.Write,
                FileShare.ReadWrite, // drives must be opened with read and write share access
                0,
                FileMode.Open,
                FileAttributes.Normal,
                IntPtr.Zero);
            if (writeHandle.IsInvalid)
                throw new Exception("Invalid Write Handle.");//Win32Exception(Marshal.GetLastWin32Error(), "Invalid SafeFileHandle: inputHandle");

            readHandle = CreateFile(_sdDrive,
                FileAccess.Read,
                FileShare.ReadWrite, // drives must be opened with read and write share access
                0,
                FileMode.Open,
                FileAttributes.Normal,
                IntPtr.Zero);
            if (readHandle.IsInvalid)
                throw new Exception("Invalid Read Handle."); //Win32Exception(Marshal.GetLastWin32Error(), "Invalid SafeFileHandle: inputHandle");

            this.ClearThread = new Thread(new ThreadStart(ClearSDprocess));
            this.ClearThread.Start();

            _isIn = true;

            return _isIn;
        }

        public ClearSD(string sdDrive, bool totalClear)
        {
            _sdDrive = @"\\.\" + sdDrive.Substring(0, 1) + ":";
            this._notTotal = !totalClear;
            clearSDInit();
        }

        public ClearSD(string sdDrive)
        {
            _sdDrive = @"\\.\" + sdDrive.Substring(0, 1) + ":";
            clearSDInit();
            /*writeHandle = CreateFile(_sdDrive,
                FileAccess.Write,
                FileShare.ReadWrite, // drives must be opened with read and write share access
                0,
                FileMode.Open,
                FileAttributes.Normal,
                IntPtr.Zero);
            if (writeHandle.IsInvalid)
                throw new Exception("Invalid Write Handle.");//Win32Exception(Marshal.GetLastWin32Error(), "Invalid SafeFileHandle: inputHandle");

            readHandle = CreateFile(_sdDrive,
                FileAccess.Read,
                FileShare.ReadWrite, // drives must be opened with read and write share access
                0,
                FileMode.Open,
                FileAttributes.Normal,
                IntPtr.Zero);
            if (readHandle.IsInvalid)
                throw new Exception("Invalid Read Handle."); //Win32Exception(Marshal.GetLastWin32Error(), "Invalid SafeFileHandle: inputHandle");

            this.ClearThread = new Thread(new ThreadStart(ClearSDprocess));
            this.ClearThread.Start();

            _isIn = true;//*/
        }

        private void ClearSDprocess()
        {
            try
            {
                using (FileStream writeStream = new FileStream(writeHandle, FileAccess.Write), readStream = new FileStream(readHandle, FileAccess.Read))
                {
                    int buffLen = 512;
                    uint sectCounter = 0;
                    byte[] buff = new byte[buffLen];
                    byte[] clearBuff = new byte[buffLen];
                    for (int i = 0; i < buffLen; i++)
                        clearBuff[i] = 0;
                    int chck;
                    int zeroheadcount = 0;
                    while (sectCounter++ < 4194303 && _isIn)//2147483648)//_isIn) 4194304
                    {
                        chck = readStream.Read(buff, 0, buffLen);
                        int headSD = ConvertUint16Data(ref buff, 0);
                        writeStream.Write(clearBuff, 0, buffLen);
                        if (_notTotal && headSD == 0)
                        {
                            zeroheadcount++;
                            if (zeroheadcount > 20)
                            {
                                writeStream.Write(clearBuff, 0, buffLen);
                                break;
                            }
                        }
                        else if (zeroheadcount != 0)
                            zeroheadcount = 0;

                        /*if (sectCounter > (4194304 / 2))
                        {
                            Console.WriteLine();
                        }
                        else
                        {
                            //sectCounter++;
                            writeStream.Write(clearBuff, 0, buffLen);
                        }*/
                    }

                    StreamWriter logout = new StreamWriter("log.out", true);
                    logout.WriteLine("SD Clearing at " + DateTime.Now.ToString("yyyy-MM-dd (HH:mm:ss)") +
                        ": cleared " + sectCounter.ToString() + " sectors.");
                    logout.Close();
                    _isIn = false;
                }
            }
            catch (System.IO.IOException ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message, "SD Card clearing error", System.Windows.Forms.MessageBoxButtons.OK);
            }
            catch
            {
                throw;
            }
            finally
            {
                _isIn = false;
            }
        }

        private UInt16 ConvertUint16Data(ref byte[] buf, int index)
        {
            byte[] val = new byte[2];
            UInt16 ret = 0;
            try
            {
                val[0] = buf[index + 1];
                val[1] = buf[index + 0];
                ret = BitConverter.ToUInt16(val, 0);
            }
            catch // (Exception e)
            {
                throw;
            }
            return ret;
        }

    }
}
